//////////////////////////////////////////////////////////////////////////////
//
// File:   authGuy.cc
//
// Description:
//   This file contains the member functions and the functionality for the
//   authGuy class.  The purpose of this class is to authenticate bnet
//   logins.
//
// Notes:
//   This file uses an extern-ed ofstream *debug that must be setup beforehand.
//
//   This class contains work that is not mine.  I do not take credit for it.
//   See the notes before each function for which ones.  (The crazy hard 
//   reverse engineered ones).
//
//
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>


#include <fstream>

#include "authGuy.h"
#include "../conGuy/buff.h"    // For print_hex_form()

using namespace std;


// These two functions taken from LGPL code written by author Maddox
// from valhallalegends.com.  These two functions do our hashing.
static void data_hash(u32 * param);
void bncs_xsha1(u32 * hash, const void * inbuf, u32 len);

extern ofstream *debug;

u32 mpqCrc[] = {
  0xE7F4CB62, 0xF6A14FFC, 0xAA5504AF,
  0x871FCDC2, 0x11BF6A18, 0xC57292E6,
  0x7927D27E, 0x2FEC8733
};

//////////////////////////////////////////////////////////////////////////////
//
// Function:  authGuy::authGuy()
//
// Description:
//   This is the default constructor for the authGuy class.  Currently it just
//   specifies the client and sets the clientToken as a random value.
//
//
authGuy::authGuy() {
  clientToken = rand();
  //  clientToken = 0x40a8972e;
  client = CLIENT_SEXP;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  authGuy::~authGuy()
//
// Description:
//   This function is here for completeness.  :D
//
authGuy::~authGuy() {
  // Nothing!
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  u32 authGuy::uatol( char *s)
//
// Description:
//   This function changes an unsigned ascii char to long (u32).
//
// Note:  
//   The default atol does not work because
//   our input is unsigned.
//
u32 authGuy::uatol( char *s) {
  u32 a = 0;

  if (!s) return( a);

  while (*s && isdigit(*s)) {
    if (a) a *= 10;
    a += *s++ - '0';
  }

  return( a);
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  u32 authGuy::checkRevision()
//
// Description:
//   Computes and returns the check revision result.
//
u32 authGuy::checkRevision() {
  u32 result;
  unsigned int mpqNum = (unsigned int)(*(strstr( hash_mpq, ".") - 1) - '0');
  *debug << "checkrevision mpqNum is: " << mpqNum << endl;
  int n = checkRevision(client, hash_equ, mpqNum, &result,
			f1.c_str(), f2.c_str(), f3.c_str());
  *debug << "Checkrevision returned: " << n << endl; 
  *debug << "Checkrevision result: ";
  for(int i = 0; i < 4; i++) {
    *debug << print_hex_form( *(((u8*)&result) +i) );
  }
  *debug << endl;
  return result;
}  // end of    u32 authGuy::checkRevision()



//////////////////////////////////////////////////////////////////////////////
//
// Function:  int authGuy::checkRevision( u32 client, char *equ, int mpq, 
//                                    u32 *var, const char *f1, const char *f2,
//                                    const char *f3)
// 
// Description:  
//   This is the beefy checkRevision algorithm (yes, sadly I stole it)
//   found online.  There was no way in hell I was going to go as far as to
//   reverse engineer this.  Have a look at the function for yourself.  GL!
//
// Variables:
// 
//  *var is the return value (result) of the checkRevision algorithm.
// f1 is "starcraft.exe"
// f2 is "storm.dll"
// f3 is "battle.snp"
//
//
int authGuy::checkRevision( u32 client, char *equ, unsigned int mpq, u32 *var, const char *f1, const char *f2, const char *f3) {
  if (mpq < 0 || mpq >= sizeof(mpqCrc)) return -1;
  int nvar = 0, nhash = 0; int vars[ 16];

  //===
  // Equ: A=3066839436 B=1452667226 C=775240323 4 A=A^S B=B^C C=C^A A=A-B
  // vars[0] is 3066839436
  // vars[1] is 1452667226
  // vars[2] is 775240323
  while (*equ) {
    if (isalpha(*equ))
      nvar = (int)(toupper(*equ) - 'A');
    else {
      nhash = (int)(*equ - '0');
      if (*(++equ))
        equ++;
      else
        return( -1);
      if (nhash & 0xfffffff0) return -1;
      break;
    }

    if (*++equ == '=')
      equ++;

    vars[ nvar] = uatol(equ);
    equ = strchr( equ, ' ');
    if (!equ++) return -1;
  }

  //===
  // nvars[0] = A A ^ S
  // nvars[1] = B B ^ S
  // nvars[2] = C C ^ S
  // nvars[3] = A A - S
  int len = strlen( equ);
  int nvars[nhash][4];
  for (int i = 0; i < nhash; i++, equ++) {
    if (len < 5) return -1;
    for (int o = 0; o < 4; o++ ? equ++ : (++equ)++)
      if (o == 2)
        nvars[i][o] = *equ;
      else {
        nvars[i][o] = *equ == 'S' ? (nhash - 1) : (toupper(*equ) - 'A');
        if (nvars[i][o] > nhash) return -1;
      }

    len -= 5;
  }


  vars[0] ^= mpqCrc[mpq];

  //===
  // Open the hash files, in reality all hash files will be opened already
  // and we will call something else, but that's for another day.
  const char *files[3];
  files[0] = f1;
  files[1] = f2;
  files[2] = f3;

  //char *files[3] = { "./Binaries/war2/Warcraft II BNE.exe",
  //                   "./Binaries/war2/storm.dll",
  //                   "./Binaries/war2/battle.snp"
  //                 };
  int size[3];
  char *data[3];
  for (int i = 0; i < 3; i++) {
    int fd = open(files[i], O_RDONLY);
    if (!fd) return -1;
    size[i] = lseek( fd, 0, SEEK_END);
    data[i] = new char[ size[i]];
    // printf("Opened %s is %d bytes\n", files[i], size[i]);

    lseek( fd, 0, SEEK_SET);
    int tmp = size[i];
    char *tbuf = data[i];
    while (tmp > 0) {
      int n = read( fd, tbuf, 4096);
      if (n > 0) {
        tbuf += n;
        tmp -= n;
      }
      //printf("%d bytes left\n", tmp);
    }

    close( fd);
  }


  for (int i = 0; i < (nhash-1); i++) {
    int *buf = (int *)data[i];
    int len = size[i];

    len = (len / 1024) * 1024;
    len /= 4;

    for (int j = 0; j < len; j++) {
      vars[3] = buf[j];
      for (int k = 0; k < nhash; k++)
        switch(nvars[k][2]) {
          case '+': 
	    vars[nvars[k][0]] = vars[nvars[k][1]] + vars[nvars[k][3]]; 
	    break;
          case '-': 
	    vars[nvars[k][0]] = vars[nvars[k][1]] - vars[nvars[k][3]]; 
	    break;
          case '^': 
	    vars[nvars[k][0]] = vars[nvars[k][1]] ^ vars[nvars[k][3]]; 
	    break;
        }
    }
  }

  for (int i = 0; i < 3; i++)
    delete data[i];

  *var = vars[2];

  return( 0);
}  //  end of     int authGuy::checkRevision(... );


//////////////////////////////////////////////////////////////////////////////
//
// Function:  bool authGuy::getCDVars()
//
// Description:  
//   This function takes the private member cdkey string as input & uses it to
//   store the cdkey variables: cdkeyVar0, cdkeyVar1, cdkeyVar2.
//
bool authGuy::getCDVars() {

  if(cdkey.length() != 13) {
    *debug << "ERROR:  Cdkey length isn't 13.  Are you sure you typed only"
	   << endl << "         numbers? (no dashes or spaces)." << endl;
    return false;
  }
  char buffer[14];
  strcpy(buffer, cdkey.c_str());
  
  
  decodeStar( buffer);
  //  *debug << "DEBUG:  getCDVars() - cdkey is now \"" << buffer << "\"" << endl;
  
  sscanf( buffer, "%2X%7d%3d", &cdkeyVar0, &cdkeyVar1, &cdkeyVar2);

  return true;
}  // end of    bool authGuy::getCDVars() 



//////////////////////////////////////////////////////////////////////////////
//
// Function:  void authGuy::decodeStar( char *cdkey)
// 
// Description:
//   Decodes the cdkey string into another form that is used to extract
//   the cdkey variables and puts that string into char* cdkey. See getCDVars()
//   for it's usage.
//
// Note:
//   I stole this function from the internet.  
//
void authGuy::decodeStar( char *cdkey) {
  //===
  // If key is ver then the key is valid
  u32 ver = 3;
  for (int i = 0; i < 12; i++)
    ver += fromhex( cdkey[i]) ^ (ver << 1);
  ver %= 10;

  //===
  // Do we care if it's valid?

  //===
  // Shifts
  ver = 194;
  for (int i = 11; (ver >= 7) && (i >= 0); i--) {
    char ch = cdkey[i];
    cdkey[i] = cdkey[ver % 12];
    cdkey[ver % 12] = ch;
    ver -= 17;
  }

  //===
  // Figure out the product
  u32 ver2 = 0x13ac9741;
  for (int i = 11; i >= 0; i--) {
    char ch = toupper(cdkey[i]);
    if (ch < '8') {
      ver = ver2;
      char tmp = ver & 0x7;
      tmp ^= ch;
      ver >>= 3;
      cdkey[i] = tmp;
      ver2 = ver;
    } else if (ch < 'A')
      cdkey[i] = (i & 1) ^ ch;
  }
} // end of   void authGuy::decodeStar( char *cdkey)


//////////////////////////////////////////////////////////////////////////////
// The below commented out sha1_hash() has faulty hash logic somewhere.  :(.


// //////////////////////////////////////////////////////////////////////////////
// //
// // Function:  void authGuy::sha1_hash( const char *src, int len, char *dest)
// //
// // Description:
// //   Creates a 5*4 bytes hash and puts it in dest.
// // 
// // Notes:
// //   Yeah, I stole this too.
// //
// void authGuy::sha1_hash( const char *src, int len, char *dest) {
//   u8 buf[ 1024];
//   bzero( buf, 1024);
//   memcpy( buf, src, len);

//   u32 *pbuf = (u32 *)buf;
//   int i = 0;
//   for (; i < 64; i++)
//     pbuf[i+16] = ROL(1, (pbuf[i] ^ pbuf[i+8] ^ pbuf[i+2] ^ pbuf[i+13]) % 32);

//   u32 a = 0x67452301;
//   u32 b = 0xefcdab89;
//   u32 c = 0x98badcfe;
//   u32 d = 0x10325476;
//   u32 e = 0xc3d2e1f0;
  
//   for (i ^= i; i < 20; i++) {
//     u32 f = pbuf[i] + ROL(a,5) + e + ((b & c) | (~b & d)) + 0x5a827999;
//     e = d;
//     d = c;
//     c = ROL(b,30);
//     b = a;
//     a = f;
//   }
  
//   for (; i < 40; i++) {
//     u32 f = (d ^ c ^ b) + e + ROL(f,5) + pbuf[i] + 0x6ed9eba1;
//     e = d;
//     d = c;
//     c = ROL(b,30);
//     b = a;
//     a = f;
//   }
                              
//   for (; i < (20 * 3); i++) {
//     u32 f = pbuf[i] + ROL(f,5) + e + ((c & b) | (d & c) | (d & b)) - 0x70e44324;
//     e = d;
//     d = c;
//     c = ROL(b,30);
//     b = a;
//     a = f;
//   }

//   u32 f;
//   for (; i < (20 * 4); i++) {
//     f = (d ^ c ^ b) + e + ROL(f,5) + pbuf[i] - 0x359d3e2a;
//     e = d;
//     d = c;
//     c = ROL(b,30);
//     b = a;
//     a = f;
//   }

//   u32 *pdest = (u32 *)dest;

//   pdest[0] = 0x67452301 + f;
//   pdest[1] = 0xefcdab89 + b;
//   pdest[2] = 0x98badcfe + c;
//   pdest[3] = 0x10325476 + d;
//   pdest[4] = 0xc3d2e1f0 + e;
// }  // end of     void authGuy::sha1_hash( const char *src, int len, char *dest)


//////////////////////////////////////////////////////////////////////////////
// This function just calls the bncs_xsha1 function below.
//
void authGuy::sha1_hash( const char *src, int len, char *dest) {
  bncs_xsha1((u32 *) dest, (void *)src, (u32) len);
}

//////////////////////////////////////////////////////////////////////////////
// The next two functions:
//
// void bncs_xsha1(u32 * hash, const void * inbuf, u32 len)
// static void data_hash(u32 * param)
//
// Were taken from Maddox from ValhallaLegends.com.  My hash code is
// wrong somewhere... but this one is correct.  You can see my
// original one above commented out.
//////////////////////////////////////////////////////////////////////////////

void bncs_xsha1(u32 * hash, const void * inbuf, u32 len)
{
	char *buf = (char*)inbuf;
	u32 pos, sublen;

	u32 hashbuf[0x10 + 5];
	hashbuf[0] = 0x67452301;
	hashbuf[1] = 0xEFCDAB89;
	hashbuf[2] = 0x98BADCFE;
	hashbuf[3] = 0x10325476;
	hashbuf[4] = 0xC3D2E1F0;

	for(pos=0; pos<len; pos+=0x40) {
		sublen = len - pos;
		if(sublen > 0x40)
			sublen = 0x40;
		memcpy(hashbuf+5, buf+pos, sublen);
		if(sublen<0x40)
			memset((char*)(hashbuf+5)+sublen, 0, 0x40-sublen);
		data_hash(hashbuf);
	}
	memcpy(hash, hashbuf, 5*4);
}

static void data_hash(u32 * param)
{
	u32 buf[0x50];
	u32 dw, a, b, c, d, e, *p;
	int i;
	memcpy(buf, param+5, 0x40);
	for(i=0x10; i<0x50; i++) {
		dw = buf[i-0x10]^buf[i-0x8]^buf[i-0xe]^buf[i-0x3];
		buf[i] = (1>>(0x20-(u8)dw)) | (1<<(u8)dw);		
	}
	a = param[0];
	b = param[1];
	c = param[2];
	d = param[3];
	e = param[4];
	p = buf;
	i = 0x14;
	do {
		dw = ROL(a, 5) + ((~b & d) | (c & b)) + e + *p++ + 0x5A827999;
		e = d;
		d = c;
		c = (b>>2)  | (b<<0x1e);
		b = a;
		a = dw;
	} while(--i);
	i = 0x14;
	do {
		dw = ROL(a, 5) + (d ^ c ^ b) + e + *p++ + 0x6ED9EBA1;
		e = d;
		d = c;
		c = (b>>2) | (b<<0x1e);
		b = a;
		a = dw;
	} while(--i);
	i = 0x14;
	do {
		dw = ROL(a, 5) + ((c & b) | (d & c) | (d & b)) + e + *p++ - 0x70E44324;
		e = d;
		d = c;
		c = (b>>2) | (b<<0x1e);
		b = a;
		a = dw;
	} while(--i);

	i = 0x14;
	do {
		dw = ROL(a, 5) + e + (d ^ c ^ b) + *p++ - 0x359D3E2A;
		e = d;
		d = c;
		c = (b>>2) | (b<<0x1e);
		b = a;
		a = dw;
	} while(--i);
	param[0] += a;
	param[1] += b;
	param[2] += c;
	param[3] += d;
	param[4] += e;
}

// End of Maddox's code.
//////////////////////////////////////////////////////////////////////////////





//////////////////////////////////////////////////////////////////////////////
//
// Function:  int authGuy::tohex( u32 i)
//
// Description:
//   Self-explanitory.
//
int authGuy::tohex( u32 i) {
  i &= 0xf;
  return i < 10 ? i + 0x30 : i + 0x37;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function:  int authGuy::fromhex( char ch)
//
// Description:
//   Self-explanitory.
//
int authGuy::fromhex( char ch) {
  ch = toupper(ch);
  return isdigit(ch) ? ch - 0x30 : ch - 0x37;
}


//////////////////////////////////////////////////////////////////////////////
//
// Function:  u32 *authGuy::getHashData()
//
// Description:
//   This function is used to get the hash data for the hash files
//   and is placed in packet SID_AUTH_CHECK.
//
// Return value:  
//   Returns a pointer to an array of 5 u32's
//
u32 *authGuy::getHashData() {
  // Working buffer.
  u32 toHash[6];

  toHash[0] = clientToken;
  toHash[1] = serverToken;
  toHash[2] = cdkeyVar0;
  toHash[3] = cdkeyVar1;
  toHash[4] = 0x00000000;
  toHash[5] = cdkeyVar2;

  // Hash da junx
  sha1_hash((char*)toHash, sizeof(u32) * 6, (char*)hash_data);
  
  // here ya go!
  return hash_data;
}  // end of    u32 *authGuy::getHashData()


//////////////////////////////////////////////////////////////////////////////
//
// Function:  u32 *authGuy::getPasswordHash()
//
// Description:
//   This function is used to get the password hash used in packet
//   SID_LOGONRESPONSE.  (0x29)
// 
// Return value:
//   Returns the pointer to the array of 5 u32's.
//
u32 *authGuy::getPasswordHash() {

  // Battle.net password hashes are hashed twice. First, the password is 
  // hashed by itsself, then the following data is hashed again and sent 
  // to Battle.net:
  // 
  // Client Token
  // Server Token
  // First password hash (20 bytes)
  //
  // Passwords should be converted to lower case before hashing.



  // Covert to lowercase:
  for(unsigned int i = 0; i < password.length(); i++) {
    if(isupper(password[i]) != 0) {
      // it's uppercase so convert to lowercase
      password[i] = tolower(password[i]);
    }
  }


  // hash the password:  
  sha1_hash(password.c_str(), password.length(), (char*)hash_password);
  

  // copy to the "toHash" buffer to be prepared to hash a 2nd time.
  u32 toHash[7];
  toHash[0] = clientToken;
  toHash[1] = serverToken;
  for(int i = 2; i < 7; i++)
    toHash[i] = hash_password[i-2];
  

  // hash a second time:
  sha1_hash((char*)toHash, 7 * sizeof(u32), (char*)hash_password);
  
  // return! :)
  return hash_password;
}  // end of    u32 *authGuy::getPasswordHash()

//////////////////////////////////////////////////////////////////////////////
//
// Function:   string authGuy::getExeInfo()
//
// Description:
//   This function returns the exeInfo.  (file name, size, time...etc.)
//
// Preconditions:  
//   Needs the string f1 set as the starcraft.exe file.
//
string authGuy::getExeInfo() {
  string rv;
  int exe_size;
  struct stat exe_info;
  time_t modification_time;
  struct tm *mod_time;

  char exe_info_str[128];

  ifstream exefile( f1.c_str());  // This should be starcraft.exe

  stat( f1.c_str(), &exe_info);
  modification_time = exe_info.st_mtime;
  mod_time = localtime(&modification_time);


  // How much hardcoding can we do?  Does it only look at exe size?
  exefile.seekg(0, ios::end);
  exe_size = exefile.tellg();
  exefile.close();


  sprintf(exe_info_str, "starcraft.exe %02d/%02d/%02d %02d:%02d:%02d %d",
	  mod_time->tm_mon+1,  mod_time->tm_mday, mod_time->tm_year - 100,
	  mod_time->tm_hour,   mod_time->tm_min,  mod_time->tm_sec,
	  exe_size);
  rv = string(exe_info_str);
  
  return rv;
} // end of     string authGuy::getExeInfo()



// End of authGuy.cc
